/****************************************************************************
*
*                            Open Watcom Project
*
*    Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved.
*
*  ========================================================================
*
*    This file contains Original Code and/or Modifications of Original
*    Code as defined in and that are subject to the Sybase Open Watcom
*    Public License version 1.0 (the 'License'). You may not use this file
*    except in compliance with the License. BY USING THIS FILE YOU AGREE TO
*    ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is
*    provided with the Original Code and Modifications, and is also
*    available at www.sybase.com/developer/opensource.
*
*    The Original Code and all software distributed under the License are
*    distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
*    EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM
*    ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF
*    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR
*    NON-INFRINGEMENT. Please see the License for the specific language
*    governing rights and limitations under the License.
*
*  ========================================================================
*
* Description:  command line argument parser
*
****************************************************************************/

//#include <stdarg.h>
#include <stddef.h>
#include <ctype.h>

#include "globals.h"
#include "memalloc.h"
#include "parser.h"
#include "msgtext.h"
#include "dbgcv.h"
#include "cmdline.h"
#include "myassert.h"
#include "input.h" /* GetFNamePart() */

//#ifdef __OSI__
//  #include "ostype.h"
//#endif

#if defined(__UNIX__) || defined(__CYGWIN__) || defined(__DJGPP__)

#define HANDLECTRLZ 0
#define SWITCHCHAR 0

#else

#define HANDLECTRLZ 1
#define SWITCHCHAR 1

#endif

#ifdef __I86__
#define OPTQUAL __near
#else
#define OPTQUAL
#endif

extern char     banner_printed;

struct global_options Options = {
	/* quiet            */          FALSE,
	/* line_numbers     */          FALSE,
	/* debug_symbols    */          0,
	/* debug_ext        */          0, /* v2.10 */
	/* floating_point   */          FPO_NO_EMULATION,

	/* error_limit      */          50,
	/* no error display */          FALSE,
	/* warning_level    */          2,
	/* warning_error    */          FALSE,
#ifdef DEBUG_OUT
	/* debug            */          FALSE,
	/* nobackpatch      */          FALSE,
#if FASTPASS
	/* nofastpass       */          FALSE,
	/* print_linestore  */          FALSE,
#endif
	/* max_passes       */          0,
	/* skip_preprocessor */         0,
	/* log_all_files    */          0,
	/* dump_reswords    */          FALSE,
	/* dump_reswords_hash */        FALSE,
	/* dump_symbols     */          FALSE,
	/* dump_symbols_hash */         FALSE,
#endif
	/* names            */          {
		NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
#if BUILD_TARGET
		NULL,
#endif
#if MANGLERSUPP
		NULL
#endif
	},
	/* queues           */          { NULL, NULL, NULL },
#if COCTALS
	/* allow_c_octals        */     FALSE,
#endif
	/* no_comment_data_in_code_records */   FALSE,
	/* no_opt_farcall        */     FALSE,
	/* no_dependencies       */ //    FALSE,
#if COFF_SUPPORT
	/* no_file_entry         */     FALSE,
	/* no_static_procs       */     FALSE,
	/* no_section_aux_entry  */     FALSE,
#endif
	/* no_cdecl_decoration   */     FALSE,
	/* stdcall_decoration    */     STDCALL_FULL,
	/* no_export_decoration  */     FALSE,
	/* entry_decorated       */     FALSE,
	/* write_listing         */     FALSE,
	/* write_impdef          */     FALSE,
	/* case_sensitive        */     FALSE,
	/* convert_uppercase     */     FALSE,
	/* preprocessor_stdout   */     FALSE,
	/* masm51_compat         */     FALSE,
	/* strict_masm_compat    */     FALSE,
	/* masm_compat_gencode   */     FALSE,
	/* masm8_proc_visibility */     FALSE,

	/* listif                */     FALSE,
	/* list_generated_code   */     FALSE,
	/* list_macro            */     LM_LISTMACRO,
	/* no_symbol_listing     */     FALSE,
	/* first_pass_listing    */     FALSE,
	/* no_final_msg_listing  */     FALSE, /* v2.13 */

	/* all_symbols_public    */     FALSE,
	/* safeseh               */     FALSE,
	/* ignore_include        */     FALSE,
	/* output_format         */     OFORMAT_OMF,
	/* sub_format            */     SFORMAT_NONE,
	/* alignment_default     */     0,
	/* langtype              */     LANG_NONE,
	/* model                 */     MODEL_NONE,
	/* cpu                   */     P_86,
	/* fastcall type         */     FCT_MSC,
	/* syntax check only     */     FALSE,
#if MANGLERSUPP
	/* naming_convention*/          NC_DO_NOTHING,
#endif
};

char *DefaultDir[NUM_FILE_TYPES] = { NULL, NULL, NULL, NULL };
//static char *DefaultExt[NUM_FILE_TYPES] = { OBJ_EXT, LST_EXT, ERR_EXT };

#define MAX_RSP_NESTING 15  /* nesting of response files */

static unsigned         OptValue;  /* value of option's numeric argument  */
static char             *OptName;  /* value of option's name argument     */
static const char       *cmdsave[MAX_RSP_NESTING]; /* response files */
static const char       *cmdbuffers[MAX_RSP_NESTING]; /* response files */
static int              rspidx = 0; /* response file level */

/* array for options -0 ... -10 */
static const enum cpu_info cpuoption[] = {
	P_86, P_186, P_286, P_386,          /* 0-3 */
	P_486, P_586, P_686, P_686 | P_MMX, /* 4-7 */
	P_686 | P_MMX | P_SSE1,             /* 8   */
	P_686 | P_MMX | P_SSE1 | P_SSE2,    /* 9   */
#if AMD64_SUPPORT
	P_64,                               /* 10  */
#endif
};

#if 0 /* v2.10: removed, because factually never called */
static void StripQuotes( char *fname )
/************************************/
{
	char *s;
	char *d;

	if( *fname == '"' ) {
		/* string will shrink so we can reduce in place */
		d = fname;
		for( s = d + 1; *s && *s != '"'; ++s ) {
			/* collapse double backslashes, only then look for escaped quotes */
			if( s[0] == '\\' && s[1] == '\\' ) {
				++s;
			} else if( s[0] == '\\' && s[1] == '"' ) {
				++s;
			}
			*d++ = *s;
		}
		*d = '\0';
	}
}

static char *GetAFileName( void )
/*******************************/
{
	DebugMsg(("GetAFileName() enter, OptName=>%s<\n", OptName ));
	StripQuotes( OptName );
	return( OptName );
}
#else
#define GetAFileName() OptName
#endif

#if BUILD_TARGET
static void SetTargName( char *name, unsigned len )
/*************************************************/
{
	if( Options.names[OPTN_BUILD_TARGET] != NULL ) {
		MemFree( Options.names[OPTN_BUILD_TARGET] );
		Options.names[OPTN_BUILD_TARGET] = NULL;
	}
	if( name == NULL || len == 0 )
		return;
	Options.names[OPTN_BUILD_TARGET] = MemAlloc( len + 1 );
	strcpy( Options.names[OPTN_BUILD_TARGET], name );
	_strupr( Options.names[OPTN_BUILD_TARGET] );
}
#endif

struct st_fn
{
	char* names[OPTN_LAST];
	struct st_fn* next;
};

static struct st_fn* st_fn_buf;
static struct st_fn* st_fn_buf_pos;

/* called by -0, -1, ... argument */

static void SetCpuCmdline( enum cpu_info value, const char *parm )
/****************************************************************/
{

	Options.cpu &= ~(P_CPU_MASK | P_EXT_MASK | P_PM);
	Options.cpu |= value;

	for( ; *parm ; parm++ ) {
		if( *parm == 'p' && Options.cpu >= P_286 ) {
			Options.cpu |= P_PM;      /* set privileged mode */
#if MANGLERSUPP
		} else if( *parm == '"' ) {       /* set default mangler */
			char *dest;
			parm++;
			dest = strchr( parm, '"' );
			if( Options.names[OPTN_DEFNAME_MANGLER] != NULL ) {
				MemFree( Options.names[OPTN_DEFNAME_MANGLER );
			}
			Options.names[OPTN_DEFNAME_MANGLER = MemAlloc( dest - parm + 1 );
			dest = Options.names[OPTN_DEFNAME_MANGLER];
			for( ; *parm != '"'; dest++, parm++ ) {
				*dest = *parm;
			}
			*dest = NULLC;
#endif
		} else {
			EmitWarn( 1, CPU_OPTION_INVALID, parm );
			break;
		}
	}
}

/* queue a text macro, include path or "forced" include files.
   this is called for cmdline options -D, -I and -Fi
 */

static void queue_item( int i, const char *string )
/*************************************************/
{
	struct qitem *p;
	struct qitem *q;

	DebugMsg(("queue_item(%u, %s) enter\n", i, string));
	p = MemAlloc( sizeof(struct qitem) + strlen( string ) );
	p->next = NULL;
	strcpy( p->value, string );
	q = Options.queues[i];
	if ( q ) {
		for ( ; q->next; q = q->next );
		q->next = p;
	} else
		Options.queues[i] = p;
	return;
}

void fun_init_fn_buf()
{
	if (st_fn_buf == NULL) {
		st_fn_buf_pos = st_fn_buf = MemAlloc(sizeof(struct st_fn));
		memset(st_fn_buf, 0, sizeof(struct st_fn));
	}
}

void fun_set_fn_buf(int type, const char* name)
{
	if (st_fn_buf_pos->names[type] == NULL) {
		(st_fn_buf_pos)->names[type] = MemAlloc(strlen(name) + 1);
		strcpy(st_fn_buf_pos->names[type], name);
		if (type == OPTN_ASM_FN)
		{
			struct st_fn* tmp = MemAlloc(sizeof(struct st_fn));
			memset(tmp, 0, sizeof(struct st_fn));
			st_fn_buf_pos = st_fn_buf_pos->next = tmp;
		}
	}else{
		MemFree(st_fn_buf_pos->names[type]);
		st_fn_buf_pos->names[type] = MemAlloc(strlen(name) + 1);
		strcpy(st_fn_buf_pos->names[type], name);
	}
}

bool fun_get_fn_buf()
{
	if (st_fn_buf != NULL) {
		st_fn_buf_pos = st_fn_buf;
		if (st_fn_buf_pos->names[OPTN_ASM_FN])
		{
			for (enum opt_names type = OPTN_ASM_FN; type < OPTN_LAST; type++)
			{
				if (Options.names[type] != NULL) {
					MemFree(Options.names[type]);
				}
				if (st_fn_buf_pos->names[type]!=NULL) {
					Options.names[type] = st_fn_buf_pos->names[type];
					st_fn_buf_pos->names[type] = NULL;
				}
			}
			st_fn_buf = st_fn_buf->next;
			MemFree(st_fn_buf_pos);
			st_fn_buf_pos = NULL;
			return TRUE;
		}else{
			for (enum opt_names type = OPTN_ASM_FN; type < OPTN_LAST; type++)
			{
				if (st_fn_buf_pos->names[type] != NULL) {
					EmitWarn(1, TOKEN_IGNORED, st_fn_buf_pos->names[type]);
					MemFree(st_fn_buf_pos->names[type]);
				}	
			}
			st_fn_buf = st_fn_buf->next;
			MemFree(st_fn_buf_pos);
			st_fn_buf_pos = NULL;
			return FALSE;
		}
	}
	return FALSE;
}

static void get_fname( int type, const char *token )
/**************************************************/
/*
 * called by -Fo, -Fw or -Fl (for .OBJ, .ERR or .LST filenames ).
 * also called by -Fd; in this case type is > NUM_FILE_TYPES!
 * v2.12: _splitpath()/_makepath() removed.
 */
{
	const char  *pName;
	char        name[FILENAME_MAX]={0};

	DebugMsg(("get_fname( type=%u, >%s< ) enter\n", type, token ));
	//_splitpath( token, drive, dir, fname, ext );
	pName = GetFNamePart( token );
	/*
	 * If name's ending with a '\' (or '/' in Unix), it's supposed
	 * to be a directory name only.
	 */
	if( *pName == NULLC ) {
		DebugMsg(("get_fname(%u, >%s< ) name is empty or a directory\n", type, token ));
		/* v2.10: ensure type is < NUM_FILE_TYPES */
		if ( type < NUM_FILE_TYPES ) {
			if ( DefaultDir[type] )
				MemFree( DefaultDir[type]);
			DefaultDir[type] = MemAlloc( strlen( token ) + 1 );
			strcpy( DefaultDir[type], token );
		}
		return;
	}
	/* v2.10: ensure type is < NUM_FILE_TYPES */
	//if ( drive[0] == NULLC && dir[0] == NULLC && type < NUM_FILE_TYPES && DefaultDir[type] ) {
	name[0] = NULLC;
	if ( pName == token && type < NUM_FILE_TYPES && DefaultDir[type] ) {
		DebugMsg(("get_fname: default drive+dir used: %s\n" ));
		//_splitpath( DefaultDir[type], drive, dir, NULL, NULL );
		strcpy( name, DefaultDir[type] );
	}
	strcat( name, token );
#if 0 /* v2.12: extension will be set in SetFileNames() */
	if( type && type < NUM_FILE_TYPES ) {
		char *pExt = GetExtPart( name );
		if ( *pExt == NULLC ) {
			*pExt++ = '.';
			strcpy( pExt, DefaultExt[type-1] );
		}
	}
#endif
	//_makepath( name, drive, dir, fname, pExt );
	fun_set_fn_buf(type, name);
}

static void set_option_n_name( int idx, const char *name )
/********************************************************/
/* option -n: set name of
 * - nd: data seg
 * - nm: module name
 * - nt: text seg
 * - nc: code class
 */
{
	if ( *name != '.' && !is_valid_id_char( *name ) ) {
		EmitError( N_OPTION_NEEDS_A_NAME_PARAMETER );
		return;
	}

	if( Options.names[idx] != NULL ) {
		MemFree( Options.names[idx] );
	}
	Options.names[idx] = MemAlloc( strlen( name ) + 1 );
	strcpy( Options.names[idx], name );
}

//static void OPTQUAL Ignore( void ) {};

#if BUILD_TARGET
static void OPTQUAL Set_bt( void ) { SetTargName( OptName,  strlen(OptName) ); }
#endif

static void OPTQUAL Set_c( void ) { }

#ifdef DEBUG_OUT
static void OPTQUAL Set_ce( void ) { rspidx = 1 / rspidx; }
#endif

static void OPTQUAL Set_Cp( void ) { Options.case_sensitive = TRUE;   Options.convert_uppercase = FALSE; }
static void OPTQUAL Set_Cu( void ) { Options.case_sensitive = FALSE;  Options.convert_uppercase = TRUE;  }
static void OPTQUAL Set_Cx( void ) { Options.case_sensitive = FALSE;  Options.convert_uppercase = FALSE; }

static void OPTQUAL Set_Zd( void ) { Options.line_numbers = TRUE; }
static void OPTQUAL Set_Zi( void )
{
	Set_Zd();
	Options.debug_symbols = CV_SIGNATURE;
	/* v2.10: added optional numeric argument for -Zi */
	if ( OptValue <= CVEX_MAX )
		Options.debug_ext = OptValue;
	else
		EmitWarn( 1, INVALID_CMDLINE_VALUE, "Zi" );
}

static void OPTQUAL Set_Zp( void )
/********************************/
{
	uint_8 power;
	for ( power = 0; (1 << power) <= MAX_STRUCT_ALIGN; power++ )
		if ( ( 1 << power ) == OptValue ) {
			Options.fieldalign = power;
			return;
		}
	EmitWarn( 1, INVALID_CMDLINE_VALUE, "Zp" );
	return;
}

static void OPTQUAL Set_D( void )  { queue_item( OPTQ_MACRO,    GetAFileName() ); }
static void OPTQUAL Set_Fi( void ) { queue_item( OPTQ_FINCLUDE, GetAFileName() ); }
static void OPTQUAL Set_I( void )  { queue_item( OPTQ_INCPATH,  GetAFileName() ); }

static void OPTQUAL Set_e( void ) { Options.error_limit = OptValue; }

static void OPTQUAL Set_nologo( void ) { banner_printed = TRUE; }
static void OPTQUAL Set_q( void )      { Set_nologo(); Options.quiet = TRUE; }
static void OPTQUAL Set_EP( void ) { Options.preprocessor_stdout = TRUE; Set_q(); }

#if DLLIMPORT
static void OPTQUAL Set_Fd( void ) { get_fname( OPTN_LNKDEF_FN, GetAFileName() ); Options.write_impdef = TRUE;}
#endif
static void OPTQUAL Set_Fw( void ) { get_fname( OPTN_ERR_FN, GetAFileName() ); }
static void OPTQUAL Set_Fl( void ) { get_fname( OPTN_LST_FN, GetAFileName() ); Options.write_listing = TRUE;}
static void OPTQUAL Set_Fo( void ) { get_fname( OPTN_OBJ_FN, GetAFileName() ); }

static void OPTQUAL Set_fp( void ) { Options.cpu &= ~P_FPU_MASK; Options.cpu = OptValue; }
static void OPTQUAL Set_FPx( void ) { Options.floating_point = OptValue; }
static void OPTQUAL Set_G( void ) { Options.langtype = OptValue; }

static void OPTQUAL Set_Sa( void )
/********************************/
{
	Options.listif = TRUE;
	Options.list_generated_code = TRUE;
	Options.list_macro = LM_LISTMACROALL;
}

static void OPTQUAL Set_True( void )
/**********************************/
{
	char *p = ((char *)&Options) + OptValue;
	*p = TRUE;
}

static void OPTQUAL Set_m( void ) { Options.model = OptValue; }
static void OPTQUAL Set_n( void ) { set_option_n_name( OptValue, OptName ); }

#ifdef DEBUG_OUT
static void OPTQUAL Set_pm( void ) { Options.max_passes = OptValue; };
#endif

static void OPTQUAL Set_WX( void ) { Options.warning_error = TRUE; }

static void OPTQUAL Set_w( void ) { Set_WX(); Options.warning_level = 0; }

static void OPTQUAL Set_W( void )
/*******************************/
{
	if ( OptValue <= 4 )
		Options.warning_level = OptValue;
	else
		EmitWarn( 1, INVALID_CMDLINE_VALUE, "W" );
}

static void OPTQUAL Set_ofmt( void )
/**********************************/
{
	Options.output_format = OptValue & 0xff;
	Options.sub_format = OptValue >> 8;
}

static void OPTQUAL Set_zcm( void ) { Options.no_cdecl_decoration = FALSE; }
#if OWFC_SUPPORT
static void OPTQUAL Set_zf( void )  { Options.fctype = OptValue; }
#endif

static void OPTQUAL Set_zt( void ) { Options.stdcall_decoration = OptValue; }
#ifndef __SW_BD
static void OPTQUAL Set_h( void ) {  PrintUsage();  exit(1); }
#endif

#ifdef DEBUG_OUT
static void OPTQUAL Set_dm( void )
{
	int i;
	for ( i = 0; i < MSG_LAST; i++ ) {
		printf("%3u: %s\n", i, MsgGetEx(i) );
	}
}
static void OPTQUAL Set_dt( void )
/********************************/
{
	Options.debug = TRUE;
	ModuleInfo.cref = TRUE; /* enable debug displays */
	DebugMsg(( "debugging output on\n" ));
}
#if FASTPASS
static void OPTQUAL Set_nfp( void )
/*********************************/
{
	Options.nofastpass = TRUE;
	DebugMsg(( "FASTPASS disabled\n" ));
}
#endif
static void OPTQUAL Set_nbp( void )
/*********************************/
{
	Options.nobackpatch = TRUE;
	DebugMsg(( "backpatching disabled\n" ));
}
#endif

struct  cmdloption {
	const char  *name;
	unsigned    value;
	void OPTQUAL (*function)( void );
};

#define optofs( x ) offsetof( struct global_options, x )

/*
 * '#': collect a number
 * '$': collect an identifer[=value]
 * '@': collect a filename
 * '=': collect an optional '='
 * '^': skip spaces before argument
 */
static struct cmdloption const cmdl_options[] = {
#ifndef __SW_BD
	{ "?",      0,        Set_h },
#endif
#ifdef DEBUG_OUT
	{ "af",     optofs( log_all_files ), Set_True },
#endif
#if BIN_SUPPORT
	{ "bin",    OFORMAT_BIN | (SFORMAT_NONE << 8), Set_ofmt },
#endif
#if BUILD_TARGET
	{ "bt=$",   0,        Set_bt },
#endif
	{ "Cp",     0,        Set_Cp },
	{ "Cu",     0,        Set_Cu },
	{ "Cx",     0,        Set_Cx },
#ifdef DEBUG_OUT
	{ "ce",     0,        Set_ce },
#endif
#if COFF_SUPPORT
	{ "coff",   OFORMAT_COFF | (SFORMAT_NONE << 8), Set_ofmt },
#endif
	{ "c",      0,        Set_c },
#if COFF_SUPPORT && DJGPP_SUPPORT
	{ "djgpp",  OFORMAT_COFF | (SFORMAT_DJGPP << 8), Set_ofmt },
#endif
#ifdef DEBUG_OUT
	{ "dm",     0,        Set_dm },
	{ "drh",    optofs( dump_reswords_hash ), Set_True },
	{ "dr",     optofs( dump_reswords ),    Set_True },
	{ "dsh",    optofs( dump_symbols_hash ), Set_True },
	{ "ds",     optofs( dump_symbols ),     Set_True },
	{ "dt",     0,        Set_dt },
#endif
	{ "D^$",    0,        Set_D },
#if ELF_SUPPORT
#if AMD64_SUPPORT
	{ "elf64",  OFORMAT_ELF | (SFORMAT_64BIT << 8), Set_ofmt },
#endif
	{ "elf",    OFORMAT_ELF | (SFORMAT_NONE << 8), Set_ofmt },
#endif
	{ "EP",     0,        Set_EP },
	{ "eq",     optofs( no_error_disp ),        Set_True },
	{ "e=#",    0,        Set_e },
#if DLLIMPORT
	{ "Fd=@",   0,        Set_Fd },
#endif
	{ "Fi=^@",  0,        Set_Fi },
	{ "Fl=@",   0,        Set_Fl },
	{ "Fo=^@",  0,        Set_Fo },
	{ "FPi87",  FPO_NO_EMULATION, Set_FPx },
	{ "FPi",    FPO_EMULATION,    Set_FPx },
	{ "fp0",    P_87,     Set_fp },
	{ "fp2",    P_287,    Set_fp },
	{ "fp3",    P_387,    Set_fp },
#if 0
	{ "fp5",    P_387,    Set_fp },
	{ "fp6",    P_387,    Set_fp },
#endif
	{ "fpc",    P_NO87,   Set_fp },
	{ "Fw=^@",  0,        Set_Fw },
	{ "Gc",     LANG_PASCAL,  Set_G },
	{ "Gd",     LANG_C,       Set_G },
	{ "Gr",     LANG_FASTCALL,Set_G },
	{ "Gz",     LANG_STDCALL, Set_G },
#ifndef __SW_BD
	{ "h",      0,        Set_h },
#endif
	{ "I=^@",   0,        Set_I },
#ifdef DEBUG_OUT
#if FASTPASS
	{ "ls",     optofs( print_linestore ), Set_True },
#endif
#endif
	{ "mc",     MODEL_COMPACT, Set_m },
	{ "mf",     MODEL_FLAT,    Set_m },
	{ "mh",     MODEL_HUGE,    Set_m },
	{ "ml",     MODEL_LARGE,   Set_m },
	{ "mm",     MODEL_MEDIUM,  Set_m },
	{ "ms",     MODEL_SMALL,   Set_m },
	{ "mt",     MODEL_TINY,    Set_m },
#if BIN_SUPPORT
#if MZ_SUPPORT
	{ "mz",     OFORMAT_BIN | (SFORMAT_MZ << 8), Set_ofmt },
#endif
#endif
#ifdef DEBUG_OUT
	{ "nbp",    0,                  Set_nbp },
#endif
	{ "nc=$",   OPTN_CODE_CLASS,    Set_n },
	{ "nd=$",   OPTN_DATA_SEG,      Set_n },
#if FASTPASS && defined(DEBUG_OUT)
	{ "nfp",    0,                  Set_nfp },
#endif
	{ "nm=$",   OPTN_MODULE_NAME,   Set_n },
	{ "nologo", 0,                  Set_nologo },
	{ "nt=$",   OPTN_TEXT_SEG,      Set_n },
	{ "omf",    OFORMAT_OMF | (SFORMAT_NONE << 8), Set_ofmt },
#if COCTALS
	{ "o",      optofs( allow_c_octals ),   Set_True },
#endif
#if PE_SUPPORT
	{ "pe",     OFORMAT_BIN | (SFORMAT_PE << 8), Set_ofmt },
#endif
#ifdef DEBUG_OUT
	{ "pm=#",   0,        Set_pm },
#endif
	{ "q",      0,        Set_q },
	{ "Sa",     0,        Set_Sa },
	{ "Sf",     optofs( first_pass_listing  ), Set_True },
	{ "Sg",     optofs( list_generated_code ), Set_True },
	{ "Sn",     optofs( no_symbol_listing   ), Set_True },
	{ "Sx",     optofs( listif              ), Set_True },
	{ "Sz",     optofs( no_final_msg_listing), Set_True }, /* v2.13 */
#if COFF_SUPPORT
	{ "safeseh",optofs( safeseh ),        Set_True },
#endif
#ifdef DEBUG_OUT
	{ "sp",     optofs( skip_preprocessor ), Set_True },
#endif
	{ "WX",     0,        Set_WX },
	{ "W=#",    0,        Set_W },
#if AMD64_SUPPORT
	{ "win64",  OFORMAT_COFF | (SFORMAT_64BIT << 8), Set_ofmt },
#endif
	{ "w",      0,        Set_w },
	{ "X",      optofs( ignore_include ), Set_True },
	{ "Zd",     0,        Set_Zd },
	{ "Zf",     optofs( all_symbols_public ),  Set_True },
	{ "Zg",     optofs( masm_compat_gencode ), Set_True },
	{ "Zi=#",   CVEX_NORMAL, Set_Zi },
	{ "Zm",     optofs( masm51_compat ),      Set_True },
	{ "Zne",    optofs( strict_masm_compat ), Set_True },
	{ "Zp=#",   0,        Set_Zp },
	{ "zcm",    0,        Set_zcm },
	{ "zcw",    optofs( no_cdecl_decoration ), Set_True },
#if OWFC_SUPPORT
	{ "zf0",    FCT_MSC,     Set_zf },
	{ "zf1",    FCT_WATCOMC, Set_zf },
#endif
	{ "zlc",    optofs( no_comment_data_in_code_records ), Set_True },
	{ "zld",    optofs( no_opt_farcall ),       Set_True },
#if COFF_SUPPORT
	{ "zlf",    optofs( no_file_entry ),        Set_True },
	{ "zlp",    optofs( no_static_procs ),      Set_True }, /* v2.10: added */
	{ "zls",    optofs( no_section_aux_entry ), Set_True },
#endif
	{ "Zs",     optofs( syntax_check_only ), Set_True },
	{ "zt0",    STDCALL_NONE, Set_zt },
	{ "zt1",    STDCALL_HALF, Set_zt },
	{ "zt2",    STDCALL_FULL, Set_zt },
	{ "Zv8",    optofs( masm8_proc_visibility ), Set_True },
	{ "zze",    optofs( no_export_decoration ),  Set_True },
#if COFF_SUPPORT
	{ "zzs",    optofs( entry_decorated ),       Set_True },
#endif
//    { NULL,     0,        0 }
};

/*
 * get a "name"
 * type=@ : filename ( -Fd, -Fi, -Fl, -Fo, -Fw, -I )
 * type=$ : (macro) identifier [=value] ( -D, -nc, -nd, -nm, -nt )
 * type=0 : something else ( -0..-10 )
 */
static const char *GetNameToken( char *dst, const char *str, int max, char type )
/*******************************************************************************/
{
	bool equatefound = FALSE;

	DebugMsg(("GetNameToken( %.*s, %u, '%c' ) enter, rspidx=%u\n", max, str, max, type, rspidx ));
	//while( isspace( *str ) ) ++str;  /* no spaces allowed! */
is_quote:
	if( *str == '"' ) {
		++str;
		for( ; max && *str; max-- ) {
			if ( *str == '"' ) {
				++str;
				break;
			}
			/* handle the \" case */
			if ( *str == '\\' && *(str+1) == '"' ) {
				++str;
			}
			*dst++ = *str++;
		}
	} else {
		for( ; max; max-- ) {
			/* v2.10: don't stop for white spaces */
			//if ( *str == NULLC || *str == ' ' || *str == '\t' )
			if ( *str == NULLC )
				break;
			/* v2.10: don't stop for white spaces if filename is expected and true cmdline is parsed */
			if ( ( *str == ' ' || *str == '\t' ) && ( rspidx || type != '@' ) )
				break;
			if ( type == 0 )
				if ( *str == '-'
#if SWITCHCHAR
					|| *str == '/'
#endif
				   )
					break;
			if ( *str == '=' && type == '$' && equatefound == FALSE ) {
				equatefound = TRUE;
				*dst++ = *str++;
				if (*str == '"')
					goto is_quote;
			}
			*dst++ = *str++;
		}
	}
	*dst = NULLC;
	return( str );
}

/*
 * A '@' was found in the cmdline. It's not an environment variable,
 * so check if it is a file and, if yes, read it.
 */

static char *ReadParamFile( const char *name )
/********************************************/
{
	char        *env;
	char        *str;
	FILE        *file;
	int         len;
	char        ch;

	DebugMsg(("ReadParamFile(%s) enter\n"));

	env = NULL;
	file = fopen( name, "rb" );
	if( file == NULL ) {
		/* v2.10: changed to fatal error */
		//EmitErr( CANNOT_OPEN_FILE, name, ErrnoStr() );
		Fatal( CANNOT_OPEN_FILE, name, ErrnoStr() );
		return( NULL );
	}
	len = 0;
	if ( fseek( file, 0, SEEK_END ) == 0 ) {
		len = ftell( file );
		rewind( file );
		env = MemAlloc( len + 1 );
#if defined(__GNUC__) /* gcc warns if return value of fread() is "ignored" */
		if ( fread( env, 1, len, file ) )
			; /* 2.12: semi-colon needs extra line to avoid warning msg */
#else
		fread( env, 1, len, file );
#endif
		env[len] = NULLC;
	}
	fclose( file );
	if ( len == 0)
		return( NULL );
	/* zip through characters changing \r, \n etc into ' ' */
	str = env;
	while( *str ) {
		ch = *str;
		if( ch == '\r' || ch == '\n' ) {
			*str = ' ';
		}
#if HANDLECTRLZ
		if( ch == 0x1A ) {      /* if end of file */
			*str = '\0';        /* - mark end of str */
			break;
		}
#endif
		++str;
	}
	return( env );
}

/* current cmdline string is done, get the next one! */

static const char *getnextcmdstring( const char **cmdline )
/*********************************************************/
{
	const char **src;
	const char **dst;

	/* something onto the response file stack? */
	if ( rspidx ) {
		rspidx--;
		if ( cmdbuffers[rspidx] )
			MemFree( (void *)cmdbuffers[rspidx] );
		return( cmdsave[rspidx] );
	}
	for ( dst = cmdline, src = cmdline+1; *src; )
		*dst++ = *src++;
	*dst = *src;
	return( *cmdline );
}

static const char *GetNumberArg( const char *p )
/**********************************************/
{
	OptValue = 0;
	for( ;*p >= '0' && *p <= '9'; p++ )
		OptValue = OptValue * 10 + *p - '0';
	return( p );
}

#if SWITCHCHAR
#define IsOptionDelimiter( x ) (x == ' ' || x == '-' || x == '/' || x == NULLC || x == '\t' )
#else
#define IsOptionDelimiter( x ) (x == ' ' || x == '-' || x == NULLC || x == '\t' )
#endif

/* scan option table and if option is known, process it */

static void ProcessOption( const char **cmdline, char *buffer )
/*************************************************************/
{
	int   i;
	int   j;
	const char *p = *cmdline;
	const char *opt;
	//char  c;

	DebugMsg(("ProcessOption(%s)\n", p ));

	/* numeric option (-0, -1, ... ) handled separately since
	 * the value can be >= 10.
	 */
	if ( *p >= '0' && *p <= '9' ) {
		p = GetNumberArg( p );
		if ( OptValue < sizeof(cpuoption)/sizeof(cpuoption[0]) ) {
			p = GetNameToken( buffer, p, 16, 0 ); /* get optional 'p' */
			*cmdline = p;
			SetCpuCmdline( cpuoption[OptValue], buffer );
			return;
		}
		p = *cmdline; /* v2.11: restore option pointer */
	}
	for( i = 0; i < ( sizeof(cmdl_options) / sizeof(cmdl_options[0]) ); i++ ) {
		//DebugMsg(("ProcessOption(%s): %s\n", p, opt ));
		if( *p == *cmdl_options[i].name ) {
			for ( opt = cmdl_options[i].name+1, j = 1 ; isalnum(*opt) && *opt == p[j]; opt++, j++ );
			/* make sure end of option is reached */
			if ( isalnum(*opt) )
				continue;
			p += j;
			OptValue = cmdl_options[i].value;
			//DebugMsg(("ProcessOption(%s): Option found\n", p ));
			for( ;; opt++) {
				switch ( *opt ) {
				//case '*': /* don't know what this is supposed to do? */
				case NULLC:
					if ( !IsOptionDelimiter( *p ) )
						goto opt_error_exit;
					*cmdline = p;
					cmdl_options[i].function();
					return; /* option processed successfully */
					break;
				case '#':             /* collect a number */
					if( *p >= '0' && *p <= '9' )
						p = GetNumberArg( p );
					break;
				case '$':      /* collect an identifer+value */
				case '@':      /* collect a filename */
					OptName = buffer;
#if 0  /* v2.05: removed */
					if ( rspidx )
						p = GetNameToken( buffer, p, FILENAME_MAX - 1, *opt );
					else {
						j = strlen( p );
						memcpy( buffer, p, (j >= FILENAME_MAX) ? FILENAME_MAX : j + 1 );
						p += j;
					}
#else
					/* v2.10: spaces in filename now handled inside GetNameToken() */
					p = GetNameToken( buffer, p, FILENAME_MAX - 1, *opt );
#endif
					break;
				case '=':    /* collect an optional '=' */
					if ( *p == '=' || *p == '#' )
						p++;
					break;
				case '^':    /* skip spaces before argument */
					while ( isspace(*p) ) p++;
					if ( *p == NULLC ) {
						p = getnextcmdstring( cmdline );
						if ( p == NULL ) {
							EmitWarn( 1, MISSING_ARGUMENT_FOR_CMDLINE_OPTION );
							return;
						}
					}
					break;
				default:
					/* internal error: unknown format of option item! */
					DebugMsg(( "ProcessOption: unknown option specifier: %s\n", opt ));
					/**/myassert( 0 );
					break;
				}
			}
		}
	}
opt_error_exit:
	EmitWarn( 1, INVALID_CMDLINE_OPTION, *cmdline - 1 );
	*cmdline = "";
	return;
}

#if BUILD_TARGET

#define MAX_OS_NAME_SIZE 7

static void set_default_build_target( void )
/******************************************/
{

	if( Options.names[OPTN_BUILD_TARGET] == NULL ) {
		Options.names[OPTN_BUILD_TARGET] = MemAlloc( MAX_OS_NAME_SIZE + 1 );
#if defined(__OSI__)
		if( __OS == OS_DOS ) {
			strcpy( Options.names[OPTN_BUILD_TARGET], "DOS" );
		} else if( __OS == OS_OS2 ) {
			strcpy( Options.names[OPTN_BUILD_TARGET], "OS2" );
		} else if( __OS == OS_NT ) {
			strcpy( Options.names[OPTN_BUILD_TARGET], "NT" );
		} else if( __OS == OS_WIN ) {
			strcpy( Options.names[OPTN_BUILD_TARGET], "WINDOWS" );
		} else {
			strcpy( Options.names[OPTN_BUILD_TARGET], "XXX" );
		}
#elif defined(__QNX__)
		strcpy( Options.names[OPTN_BUILD_TARGET], "QNX" );
#elif defined(__LINUX__)
		strcpy( Options.names[OPTN_BUILD_TARGET], "LINUX" );
#elif defined(__BSD__)
		strcpy( Options.names[OPTN_BUILD_TARGET], "BSD" );
#elif defined(__OSX__) || defined(__APPLE__)
		strcpy( Options.names[OPTN_BUILD_TARGET], "OSX" );
#elif defined(__DOS__)
		strcpy( Options.names[OPTN_BUILD_TARGET], "DOS" );
#elif defined(__OS2__)
		strcpy( Options.names[OPTN_BUILD_TARGET], "OS2" );
#elif defined(__NT__)
		strcpy( Options.names[OPTN_BUILD_TARGET], "NT" );
#else
		#error unknown host OS
#endif
	}
	return;
}
#endif

/* parse cmdline:
 * - process cmdline options
 * - get filename argument
 * - handle (nested) response files
 */

char * EXPQUAL ParseCmdline( const char **cmdline, int *pCntArgs )
/****************************************************************/
{
	const char *str = *cmdline;
	char paramfile[FILENAME_MAX];

	fun_init_fn_buf();
	for (int i = 0; i < OPTN_ASM_FN; i++)
		if (Options.names[i] != NULL) {
			MemFree(Options.names[i]);
			Options.names[i] = NULL;
		}

	/* enable next line if debug log is to be active, but -dt cannot be set */
	//Set_dt();

	for( ; str; ) {
		switch( *str ) {
		case ' ':
		case '\t':
			str++;
			break;
		case NULLC:
			str = getnextcmdstring( cmdline );
			break;
		case '-':
#if SWITCHCHAR
		case '/':
#endif
			str++;
			*cmdline = str;
			ProcessOption( cmdline, paramfile );
			(*pCntArgs)++;
			str = *cmdline;
			break;
		case '@':
			if ( rspidx >= MAX_RSP_NESTING ) {
				EmitErr( NESTING_LEVEL_TOO_DEEP );
				*cmdline = "";
				return( NULL );
			}
			str++;
#if 1 /* v2.06: was '0' in v2.05, now '1' again since it didn't work with quoted names */
			/* todo: might be unnecessary since v.2.10, since GetNameToken() handles spaces inside filenames differently */
			if ( rspidx ) {
				cmdsave[rspidx] = GetNameToken( paramfile, str, sizeof( paramfile ) - 1, '@' );
			} else {
				strcpy( paramfile, str ); /* fixme: no overflow check */
				cmdsave[rspidx] = str + strlen(str);
			}
#else
			cmdsave[rspidx] = GetNameToken( paramfile, str, sizeof( paramfile ) - 1, '@' );
#endif
			cmdbuffers[rspidx] = NULL;
			str = NULL;
			if ( paramfile[0] )
				str = getenv( paramfile );
			if( str == NULL ) {
				str = ReadParamFile( paramfile );
				cmdbuffers[rspidx] = str;
				if ( str == NULL ) {
					str = cmdsave[rspidx];
					break;
				}
			}
			rspidx++;
			break;
		default: /* collect  file name */
#if BUILD_TARGET
			set_default_build_target();
#endif
#if 1 /* v2.06: activated (was removed in v2.05). Needed for quoted filenames */
			if ( rspidx ) {
				str = GetNameToken( paramfile, str, sizeof( paramfile ) - 1, '@' );
				get_fname( OPTN_ASM_FN, paramfile );
			} else {
				int len;
				len = strlen( str );
				get_fname( OPTN_ASM_FN, str );
				str += len;
			}
#else
			str = GetNameToken( paramfile, str, sizeof( paramfile ) - 1, '@' );
			Options.names[ASM] = MemAlloc( strlen( paramfile ) + 1 );
			strcpy( Options.names[ASM], paramfile );
#endif
			DebugMsg(("ParseCmdLine: file=>%s< rest=>%s<\n", Options.names[ASM], str ? str : "NULL" ));
			(*pCntArgs)++;
			*cmdline = str;
		}
	}
	*cmdline = str;
	return( NULL );
}

void EXPQUAL CmdlineFini( void )
/******************************/
/* Free resources allocated by cmdline options */
{
	int i;
	DebugMsg(("CmdLineFini enter\n" ));
	for ( i = 0; i < NUM_FILE_TYPES; i++ ) {
		if ( DefaultDir[i] != NULL ) {
			MemFree( DefaultDir[i] );
			DefaultDir[i] = NULL;
		}
	}
	for ( i = 0; i < OPTN_LAST; i++ )
		if ( Options.names[i] != NULL ) {
			MemFree( Options.names[i] );
			Options.names[i] = NULL;
		}
	for ( i = 0; i < OPTQ_LAST; i++ ) {
		struct qitem *p;
		struct qitem *q;
		for ( q = Options.queues[i]; q; ) {
			p = q->next;
			MemFree( q );
			q = p;
		}
		Options.queues[i] = NULL;
	}
	DebugMsg(("CmdLineFini exit\n" ));
	return;
}

